require "arknights/utils"

local _data = require("arknights/data")

-- 基建卡片界面构图
local card_offset_y = 129
local card_offset_x = 631
local card_w = 183
local card_h = 394
local card_x_stride = 216
local card_y_stride = 421

-- 进驻总览界面构图
local floor_anchor = {x=644, y=131}         -- 楼层分隔符（一楼）的初始坐标
local floor_anchor_offset = {0,0,0,0,598}   -- 每层可以实现的和anchor距离最近的坐标
local room_anchor = {x=644, y=200}
-- local room_clear_base = {1810, 300}
local room_offset = {x=442-10, y=24+10}     -- 每层第一个房间第一个角色卡本和房间锚点之间之间偏移
local floor_offset_y = 67                   -- 每层最后一个房间的角色头像和下层锚点之间滑动距离
local room_x_stride = 168                   -- 每张角色卡之间偏移
local room_y_stride = 241                   -- 每层房间之间偏移

local drone_num = {x=1096, y=45, c=0xdc92ff} -- hud上无人机数量标签，存在于基建大多界面，当前景有对话框时这个标签是不可见的

-- TODO 这里的4基于一屏只能显示4个房间的假设
local room_on_screen = 4

local in_clear_mode = false


local function click_card(i, j)
    local card_click = {
        {   
            {card_offset_x+0.5*card_x_stride, card_offset_y+0.5*card_y_stride},
            {card_offset_x+1.5*card_x_stride, card_offset_y+0.5*card_y_stride},
            {card_offset_x+2.5*card_x_stride, card_offset_y+0.5*card_y_stride},
            {card_offset_x+3.5*card_x_stride, card_offset_y+0.5*card_y_stride},
            {card_offset_x+4.5*card_x_stride, card_offset_y+0.5*card_y_stride},
            {card_offset_x+5.5*card_x_stride, card_offset_y+0.5*card_y_stride}
        },
        {
            {card_offset_x+0.5*card_x_stride, card_offset_y+1.5*card_y_stride},
            {card_offset_x+1.5*card_x_stride, card_offset_y+1.5*card_y_stride},
            {card_offset_x+2.5*card_x_stride, card_offset_y+1.5*card_y_stride},
            {card_offset_x+3.5*card_x_stride, card_offset_y+1.5*card_y_stride},
            {card_offset_x+4.5*card_x_stride, card_offset_y+1.5*card_y_stride},
            {card_offset_x+5.5*card_x_stride, card_offset_y+1.5*card_y_stride}
        }
    }
    local selected = isColor(card_click[i][j][1], card_click[i][j][2]+190, 0x299dc)
    
    -- repeat
    --     randTap(card_click[i][j][1], card_click[i][j][2], 30, 60)
    --     mSleep(300)
    -- until (selected and not isColor(card_click[i][j][1], card_click[i][j][2]+190, 0x299dc)) 
    --    or (not selected and isColor(card_click[i][j][1], card_click[i][j][2]+190, 0x299dc))

    _wait_for_cond(function ()
        return (selected and not isColor(card_click[i][j][1], card_click[i][j][2]+190, 0x299dc)) 
        or (not selected and isColor(card_click[i][j][1], card_click[i][j][2]+190, 0x299dc))
    end, 150, function ()
        randTap(card_click[i][j][1], card_click[i][j][2], 30, 60)
        mySleep(300)
    end)
end


local function click_room(i, j, anchor_offset)
    local room_base = {x=room_anchor.x+room_offset.x, y=room_anchor.y+room_offset.y+anchor_offset}
    -- 房间进驻界面
    local ro_hint = { -- 房间进驻界面左侧一黑一蓝两个方块
        {x=59, y=252, c=0x303031},
        {x=59, y=405, c=0x0092d3}
    }
    local room_click = {
        {
            room_base, 
            {x=room_base.x+room_x_stride    ,y=room_base.y},
            {x=room_base.x+room_x_stride*2  ,y=room_base.y}, 
            {x=room_base.x+room_x_stride*3  ,y=room_base.y}, 
            {x=room_base.x+room_x_stride*4  ,y=room_base.y}
        },
        {
            {x=room_base.x ,y=room_base.y+room_y_stride},
            {x=room_base.x+room_x_stride ,y=room_base.y+room_y_stride},
            {x=room_base.x+room_x_stride*2 ,y=room_base.y+room_y_stride}, 
            {x=room_base.x+room_x_stride*3 ,y=room_base.y+room_y_stride}, 
            {x=room_base.x+room_x_stride*4 ,y=room_base.y+room_y_stride}
        },
        {
            {x=room_base.x                  ,y=room_base.y+room_y_stride*2},
            {x=room_base.x+room_x_stride    ,y=room_base.y+room_y_stride*2},
            {x=room_base.x+room_x_stride*2  ,y=room_base.y+room_y_stride*2}, 
            {x=room_base.x+room_x_stride*3  ,y=room_base.y+room_y_stride*2}, 
            {x=room_base.x+room_x_stride*4  ,y=room_base.y+room_y_stride*2}
        },
        {
            {x=room_base.x                  ,y=room_base.y+room_y_stride*3},
            {x=room_base.x+room_x_stride    ,y=room_base.y+room_y_stride*3},
            {x=room_base.x+room_x_stride*2  ,y=room_base.y+room_y_stride*3}, 
            {x=room_base.x+room_x_stride*3  ,y=room_base.y+room_y_stride*3}, 
            {x=room_base.x+room_x_stride*4  ,y=room_base.y+room_y_stride*3}
        }
    }
    -- local test_miss = 2
    -- repeat
    --     test_miss = test_miss + 1
    --     if test_miss == 3 then
    --         qcRandTap(room_click[i][j], 3)
    --         test_miss = 0
    --     end
    --     mSleep(500)
    -- -- 确认点击进驻的两个条件
    -- until (in_clear_mode and isColor(room_click[i][j].x, room_click[i][j].y, 0x898989))  -- 移除模式，人被移走，图标变成灰色+
    --    or (qcIsColor(ro_hint[1]) and qcIsColor(ro_hint[2])) -- 
    
    _wait_for_cond(function ()
        return (in_clear_mode and isColor(room_click[i][j].x, room_click[i][j].y, 0x898989))  -- 移除模式，人被移走，图标变成灰色+
        or (qcIsColor(ro_hint[1]) and qcIsColor(ro_hint[2]))
    end, 300, function() 
        qcRandTap(room_click[i][j], 3)
    end)
    mSleep(700)
end


local function toggle_removal(on)
    local room_clear_toggle = {x=1650, y=70}
    local toggle_hint = {x=1640, y=100, t=60} -- 49 < x < 96
    local r = getColorRGB(1640,100)
    in_clear_mode = on
    local cnt = 0
    if r < 100 and in_clear_mode then 
        -- randTap(room_clear_toggle[1], room_clear_toggle[2], 10, 60)
        -- qcRandTap(room_clear_toggle, 10)
        -- repeat
        --     mSleep(300)
        --     r = getColorRGB(1640,100)
        --     cnt = cnt + 1
        --     if cnt == 10 then
        --         qcRandTap(room_clear_toggle, 10)
        --         cnt = 0
        --     end
        -- until r > 100
        waitForLuminOver(toggle_hint, toggle_hint.t, 150, function ()    
            qcRandTap(room_clear_toggle, 10)
        end)
    elseif r > 100 and not in_clear_mode then
        -- randTap(room_clear_toggle[1], room_clear_toggle[2], 10, 60)
        -- qcRandTap(room_clear_toggle, 10)
        -- repeat
        --     mSleep(300)
        --     r = getColorRGB(1640,100)
        --     cnt = cnt + 1
        --     if cnt == 10 then
        --         qcRandTap(room_clear_toggle, 10)
        --         cnt = 0
        --     end
        -- until r < 100
        waitForLuminUnder(toggle_hint, toggle_hint.t, 150, function ()
            qcRandTap(room_clear_toggle, 10)
        end)
    end
    mSleep(700)
end


local function reset_slide_card(nscreens)
    if nscreens > 0 then
        for i=1,2 do 
            os.execute("input swipe 600 110 15000 110 2000") 
            mSleep(500)
        end
    end
end


local function reset_slide_room()
    for i=1,2 do 
        os.execute("input swipe 1880 540 1880 5540 500") 
        mSleep(500)
    end
end


local function slide_cards(n)
    local success = false
    while not success do 
        local x = 1600
        local y = 110
        mSleep(100)
        touchDown(x,y)
        mSleep(300)
        for i=1, 18*n+2 do
            x = x - 12
            touchMove(x, y)
            mSleep(2)
        end

        local bottom = card_offset_y + card_h - 3
        x,y,success = fix_position(card_offset_x, bottom, 1, 0, x, y, 90, 110)
        mSleep(100)
        touchUp(x, y)
    end
end


local function slide_floor(rooms, cross_floor)
    local scroll_end = {1880, 1000, 110}
    local success = false
    while not success do
        local x = 1880
        local y = 800
        touchDown(x,y)
        for i = 1, 24*(rooms)+2 do
            y = y - 10
            touchMove(x, y)
            mSleep(2)
        end
        -- mSleep(1000)
        if cross_floor then
            -- move another `floor_offset`
            local extent_y = floor_offset_y
            while extent_y > 10 do
                y = y - 10
                touchMove(x, y)
                extent_y = extent_y - 10
                mSleep(20)
            end
            y = y - extent_y
            touchMove(x, y)
            if not scroll_condition_dark(scroll_end) then
                x,y,success = fix_position(floor_anchor.x, floor_anchor.y, 0, 1, x, y, 60, 210)
            else
                success = true
            end
        else
            if not scroll_condition_dark(scroll_end) then
                x,y,success = fix_position(room_anchor.x, room_anchor.y, 0, 1, x, y, 60, 210)
            else
                success = true
            end
        end
        mSleep(100)
        touchUp(x,y)
    end
end


local function validate_result(tab, s)
    local match = false
    for _, i in ipairs(tab) do
        match = i == s
        if match then break end
    end
    if not match then
        movefile(userPath() .. "/res/tmp.png", userPath().."/res/mismatch-"..os.date("%m%d%H%M%S")..".png")
        log(string.format("%s: no match for \"%s\"", os.date("%m-%d-%H:%M:%S"), s), "baiduAI")
        _data = require("arknights/data")
        mSleep(200)
    end
    return match
end

local function match_result(tab, key, s)
    return s == key or has(tab[key], s)
end

local function _get_character_name(i, j)
    local y = card_offset_y + i*card_y_stride
    local x = card_offset_x + j*card_x_stride
    x,_ = fix_coordinate(x,y+card_h, 1, 0, 80, 110)
    local name_x = x + 30
    local name_y = y + 357
    local _x, _y = name_x, name_y
    local text
    while text == nil or not validate_result(_data.checkTable, text) do
        text = baiduOcr(_x, _y, _x+155, _y+40)
        _x = name_x + math.random(6) - 3
        _y = name_y + math.random(6) - 3
    end
    -- local text = tsOcrText(dict, name_x, name_y, name_x + 145, name_y+45, "FFFFFF,5F5F5F", 90)
    -- dialog("target text "..text, 1)
    return text
end


local function get_character_name(i ,j)
    local raw_text = _get_character_name(i, j)
    for k, v in pairs(_data.clashTable) do -- clashTable is literally way too big, use carefully
        if k == raw_text or has(v, raw_text) then
            return k
        end
    end
end


local function find_and_click_characters(name_tab)
    -- 将所有名字和容错组织成表
    local tmp_tab = {}
    for _, name in ipairs(name_tab) do
        table.insert( tmp_tab, name )
        -- if there's an entry in the clashtable
        if _data.clashTable[name] ~= nil then
            for _, tols in ipairs(_data.clashTable[name]) do
                table.insert( tmp_tab, tols )
            end
        end
    end
    -- 检查每张卡片是否在名字列表中
    local hit_count = #name_tab
    local pages = 15
    while true do
        for n=0,pages do
            for j=0,5 do
                for i=0,1 do
                    if has(tmp_tab, _get_character_name(i, j)) then
                        click_card(i+1,j+1)
                        hit_count = hit_count - 1
                        if hit_count == 0 then 
                            mSleep(500)
                            return n
                        end
                    end
                end
            end
            slide_cards(6)
        end
        reset_slide_card(pages)
    end
end

local function find_and_click_characters_calc( avail, name_tab, room_type, room_size)
    if has({"gold", "exp", "ge"}, room_type) then
        room_type = "prod"
    end

    local sorted_name = {}
    local str = ""
    for _, name in ipairs(_data.priority[room_type]) do
        if has(avail, name) then
            table.insert(sorted_name, name)
            str = str.." "..name
        end
    end
    dialog("expect seq: "..str, 1)

    for idx, name in ipairs(sorted_name) do
        if has(name_tab, name) then
            dialog("expect "..name.." @ "..idx, 1)
            while idx > 12 do
                slide_cards(6)
                idx = idx - 12
            end
            idx = idx - 1 + room_size
            local x = math.floor(idx / 2) + 1
            local y = idx % 2 + 1
            click_card(y, x)
            delete(avail, name)
        end
    end
end

-- this function puts 3 guy in the front to rest
local function just_get_them_all_in(remaining)
    local lim = math.min(3, remaining)
    for idx=3,2+lim do
        idx = idx - 1
        local x = math.floor(idx / 2) + 1
        local y = idx % 2 + 1
        click_card(y, x)
    end
    return remaining - lim
end

local roomAction = {
    -- 本函数表假设已经click_room
    normal = function( states, room_name, do_click_room )
        local rest_list = states.to_rest
        local state_tab = states[room_name]
        local on_duty = state_tab.on_duty
        local shift_idx = state_tab.shift_idx
        local max_duty = _data.shift[room_name][shift_idx].max_duty
        if on_duty == max_duty then
            local next_shift_idx = shift_idx % #_data.shift[room_name] + 1
            local cur_shift = _data.shift[room_name][shift_idx].staff
            local next_shift = _data.shift[room_name][next_shift_idx].staff
            local room_size = #cur_shift
            
            do_click_room()
            -- 撤下所有人
            for i = 0,room_size-1 do
                local x = math.floor(i / 2) + 1
                local y = i % 2 + 1
                click_card(y, x)
            end
            -- 点击所有新人
            -- local ns = find_and_click_characters(next_shift)
            -- reset_slide_card(ns)
            find_and_click_characters_calc(states.rested, next_shift, room_name, room_size)

            for _, v in ipairs(cur_shift) do 
                table.insert( rest_list, v )
            end
            state_tab.on_duty = 0
            state_tab.shift_idx = next_shift_idx
            return true 
        else 
            state_tab.on_duty = on_duty + 1
            return false
        end
    end, 

    pipeline = function( states, room_name, do_click_room )
        -- the pipeline namelist indicates the sequence to be off (loop clock-wise)
        local rest_list = states.to_rest
        local state_tab = states[room_name]
        local on_duty = state_tab.on_duty
        if on_duty ~= _data.shift[room_name].max_duty then 
            local shift_idx = state_tab.shift_idx
            local cur_shift = _data.shift[room_name].staff[shift_idx]
            -- chara ahead
            local next_shift = _data.shift[room_name].staff[(shift_idx + #_data.shift[room_name].staff - 2)% #_data.shift[room_name].staff + 1]
            -- chara behind
            local next_rest_idx = shift_idx % #_data.shift[room_name].staff + 1
            local room_size = #_data.shift[room_name].staff-1

            do_click_room()
            click_card(1,1)
            -- local ns = find_and_click_characters(next_shift)
            -- reset_slide_card(ns)
            find_and_click_characters_calc(states.rested, {next_shift}, room_name, room_size)

            table.insert( rest_list, cur_shift )
            state_tab.shift_idx = next_rest_idx
            state_tab.on_duty = on_duty + 1
            return true
        else
            state_tab.on_duty = 0
            return false
        end

    end,

    binded = function( states, room_name, do_click_room )
        local rest_list = states.to_rest
        local idx = tonumber(string.sub(room_name, -1))
        local rname = string.sub(room_name, 1, -2)
        -- dialog(rname.." no. "..idx)
        local state_tab = states[rname]
        local shift_idx = state_tab[idx].shift_idx
        local on_duty = state_tab[idx].on_duty
        local max_duty = _data.shift[rname][shift_idx].max_duty
        if on_duty == max_duty then
            -- 所有的索引 - 使用中的索引 = 没有
            local next_shift_idx = #_data.shift[rname]
            for i, room in ipairs(state_tab) do
                next_shift_idx = next_shift_idx + i - room.shift_idx
            end
            local cur_shift = _data.shift[rname][shift_idx].staff
            local next_shift = _data.shift[rname][next_shift_idx].staff
            local room_size = #cur_shift
            
            do_click_room()
            -- 撤下所有人
            for i=0,room_size-1 do
                local x = math.floor(i / 2) + 1
                local y = i % 2 + 1
                click_card(y, x)
            end

            -- 点击所有新人
            -- local ns = find_and_click_characters(next_shift)
            -- reset_slide_card(ns)
            find_and_click_characters_calc(states.rested, next_shift, rname, room_size)

            for _, v in ipairs(cur_shift) do 
                table.insert( rest_list, v )
            end
            state_tab[idx].on_duty = 0
            state_tab[idx].shift_idx = next_shift_idx
            return true
        else 
            state_tab[idx].on_duty = on_duty + 1
            return false
        end
    end    
}

local function do_shift()
    -- 基建总览
    local go_button = {x=130, y=180} -- 基建总览中 Garrison Overview 的tag
    local go_hint = {x=1653, y=55, c=0x7d0022} -- GO界面右上角独特的红色按钮
    local filter_confirm = {        
        {x=1475, y=830, c=0x86c0},
        {x=1414, y=840, c=0xffffff}
    }
    local filter_option = {
        off = {x=630, y=540, c=0x383838},
        on = {x=630, y=540, c=0x86c0}
    }
    -- local function reset_slide_room()
    --     waitForColor(drone_num, 300, 
    --         function ()
    --             qcKeyBack(1000)
    --         end    
    --     )

    --     waitForColor(go_hint, 300, 
    --         function()
    --             qcRandTap(go_button,20)
    --         end
    --     )
    --     -- just in case 
    --     mSleep(1000)
    -- end

    local sft_confirm = {x=1770, y=1020}

    -- 上次轮岗状态
    local status_cache = _data.load_json(userPath() .. "/res/infra.json")

    waitForColor(go_hint, 300, 
        function()
            qcRandTap(go_button, 15)
        end
    )
    -- just in case 
    mSleep(1000)

    -- move everybody out
    for f, info in ipairs(_data.rooms) do
        local to_slide = info.nroom
        if info.dorm_idx > 0 then
            local lr = info.dorm_idx
            if lr > room_on_screen then
                slide_floor(room_on_screen, false)
                lr = lr - room_on_screen
                to_slide = to_slide - room_on_screen
            end
            toggle_removal(true)
            click_room(lr, 3, floor_anchor_offset[f])
            click_room(lr, 4, floor_anchor_offset[f])
            click_room(lr, 5, floor_anchor_offset[f])
            toggle_removal(false)
        end

        -- floor_anchor_offset 非0说明到底了，到底就可以少滑动几层
        if f < 5 then
            slide_floor(to_slide - math.floor( floor_anchor_offset[f+1] / room_y_stride ), floor_anchor_offset[f+1] == 0)
            mSleep(500)
        else
            reset_slide_room()
        end
    end

    -- pick off tired staff & add new staff
    for f, info in ipairs(_data.rooms) do
        local to_slide = info.nroom
        for r, rn in ipairs(info.rname) do
            if rn ~= "_" then 
                local lr = r
                if r > room_on_screen then
                    slide_floor(room_on_screen, false)
                    lr = r - room_on_screen
                    to_slide = to_slide - room_on_screen
                end
                -- dialog(string.format("floor %d, room %s", f, rn))
                if roomAction[info.rtype[r]](status_cache, rn, function() -- pass as function the click_room with proper arguments
                    click_room(lr, 1, floor_anchor_offset[f])
                end) then
                    -- click confirm, network transmission occurs so wait

                    waitForColor(go_hint, 600, function()
                        qcRandTap(sft_confirm, 10, 500)
                        -- qcKeyBack() -- test mode, click "back" instead of confirm
                    end)
                    -- mSleep(1200)
                end
            end
        end
        -- floor_anchor_offset 非0说明到底了，到底就可以少滑动几层
        if f < 5 then
            slide_floor(to_slide - math.floor( floor_anchor_offset[f+1] / room_y_stride ), floor_anchor_offset[f+1] == 0)
            mSleep(500)
        else
            reset_slide_room()
        end
    end
    -- put tired staff to sleep
    -- local offlist_cursor = 1
    -- local offlist = status_cache.to_rest
    local remaining = #status_cache.to_rest
    for f, info in ipairs(_data.rooms) do
        local to_slide = info.nroom
        if info.dorm_idx > 0 then
            local lr = info.dorm_idx
            if lr > room_on_screen then
                slide_floor(room_on_screen, false)
                lr = lr - room_on_screen
                to_slide = to_slide - room_on_screen
            end
            -- toggle_removal(true)
            -- click_room(lr, 3, floor_anchor_offset[f])
            -- click_room(lr, 4, floor_anchor_offset[f])
            -- click_room(lr, 5, floor_anchor_offset[f])
            -- toggle_removal(false)
            click_room(lr, 1, floor_anchor_offset[f])
            
            -- local ns = find_and_click_characters(
            --     subtab(offlist, offlist_cursor, math.min(offlist_cursor+2, #offlist))
            -- )
            -- reset_slide_card(ns)
            -- offlist_cursor = offlist_cursor+3
            
            -- check whether filter is selected
            waitForAllColors(filter_confirm, 150,  function ()
                qcRandTap({x=1760, y=60}, 10, 700)
            end)
            
            if qcIsColor(filter_option.off) then
                waitForColor(filter_option.on, 150, function ()                 
                    qcRandTap(filter_option.on, 8, 700)            
                end)
            end

            waitWhileAllColors(filter_confirm, 150, function ()
                qcRandTap(filter_confirm[2], 10, 700)
            end)
            -- putting them all in without figuring out who is who
            remaining = just_get_them_all_in(remaining)

            -- qcRandTap(sft_confirm, 10)
            -- repeat
            --     mSleep(500)
            -- until isColor(145, 540, 0x33ccff) and isColor(500, 540, 0xffffff)

            waitForColor(go_hint, 600, 
                function ()
                    qcRandTap(sft_confirm, 10, 500)
                    -- qcKeyBack()
                end    
            )
            mSleep(1500)

            -- if offlist_cursor > #offlist then
            if remaining == 0 then
                status_cache.rested = mergelist(status_cache.rested, status_cache.to_rest)
                status_cache.to_rest = {}
                break
            end
        end
        -- dorm not enough, save 4 next shift (would never happen theoretically)
        if f < 5 then
            slide_floor(to_slide - math.floor( floor_anchor_offset[f+1] / room_y_stride ), floor_anchor_offset[f+1] == 0)
            mSleep(500)
        end
    end
    -- if offlist_cursor <= #offlist then 
    --     status_cache.to_rest = subtab(offlist, offlist_cursor, #offlist)
    -- end
    -- movefile(userPath() .. "/res/infra.json", userPath() .. "/res/infra.." .. "-"..os.date("%m-%d-%H")..".json")
    movefile(userPath() .. "/res/infra.json", userPath() .. "/res/infra" .. "-"..os.date("%m-%d-%H")..".json")
    _data.dump_json(status_cache, userPath() .. "/res/infra.json")

    waitForColor(drone_num, 300, 
        function ()
            qcKeyBack(1000)
        end    
    )

    return status_cache.rested
end

local function exhaust_drones_trade()
    local room_tags = {}
    local selected_room = 1

    local trade_room = {x=304, y=480} -- 基建总览中任意一个贸易站

    local room_focus_hint = {x=1734, y=561, c=0xd5d53b} -- 贸易站里的绿色小灯
    local order_tag = {x=400, y=930} -- 房间订单总结订单，点击进入房间详情
    local drone_boost = {x=471, y=745, c=0x5d557e} -- 第一张订单中使用无人机按钮的特殊色
    local out_of_stock = {x=600, y=235, c=0xd1d2d6} -- 资源不足时的白订单
    local first_order = {x=650, y=650} -- 这个是有必要的，否则点击无人机按钮影响颜色判断

    local use_drone_confirm = {x=1472, y=872, c=0x126d96} -- drone选择窗口，确认
    local use_drone_cancel = {x=480, y=875} -- drone选择窗口，取消
    local max_drone_sel = {x=1442, y=534, c=0x535353} -- drone数量选择器中“最多”
    local reduce_drone_sel = {x=885, y=500} -- drone数量选择器中“-”
    local exceed_hint = {x=960 ,y=598 ,c=0xe86003} -- drone数量溢出时出现的橙色提示

    -- dynamically build room_tags
    for i = 0, #_data.shift.trade-2 do
        table.insert(room_tags, {x=14, y=318+i*123, c=0xacf3}) -- 贸易站界面左侧房间列表
    end

    moveZoomOut(660, 340, 1260, 740, 40)
    waitForColor(room_focus_hint, 1000,
        function()
            qcRandTap(trade_room, 30)
        end    
    )
    waitForColor(room_tags[1], 300, -- 因为选择点击第一个贸易站，第一个贸易站的tag在总览页面是选中状态
        function () 
            qcRandTap(order_tag, 50, 500)
        end
    )
    while not qcIsColor(out_of_stock) do
        -- click until all orders are dealed
        while not (qcIsColor(drone_boost)) do
            qcRandTap(first_order, 10, 1000)
        end

        -- if this kind of resourse run out then nothing to do
        if qcIsColor(drone_boost) then
            -- click drone boost
            waitForColor(use_drone_confirm, 100,
                function() 
                    qcRandTap(drone_boost, 10, 700)
                end    
            )

            -- try to click max
            if not qcIsColor(max_drone_sel) then
                -- quit
                mSleep(300)
                qcRandTap(use_drone_cancel, 20, 1000)
                qcKeyBack(700)
                qcKeyBack(1400)
                return
            else 
                qcRandTap(max_drone_sel, 10, 500)
            end

            -- drones exceeded
            if qcIsColor(exceed_hint) then
                qcRandTap(reduce_drone_sel, 10, 200)
            end

            -- confirm drone usage
            waitForColor(drone_num, 600, 
                function()
                    qcRandTap(use_drone_confirm, 20, 400)
                end
            )
        end

        -- switch room
        if #room_tags ~= 1 then
            while true do
                local coo = room_tags[selected_room]
                if not qcIsColor(coo) then
                    -- randTap(coo[1], coo[2], 10, 50)
                    qcRandTap(coo, 10, 700)
                    break
                end
                selected_room = selected_room % #room_tags + 1
            end
        end
    end
end


local function exhaust_drones_product()
    local production_room = {x=190, y=600} -- 9宫格中4号位房间，我的布局是摸金制造站
    local room_focus_hint = {x=1375, y=220, c=0xffff4f} -- 制造站右上角的日光灯管
    local room_tag_color = {0x242424, 0xffd802}

    local room_tags = {
        {x=14, y=318, c=room_tag_color[1]}, 
        {x=14, y=318+123, c=room_tag_color[1]},
        {x=14, y=318+123*2, c=room_tag_color[2]},
    }
    local detail_tag = {x=400, y=930} -- 房间生产操作界面，点击进入房间详情
    local drone_boost = {x=1832, y=810} -- 加速生产按钮
    local use_drone_confirm = {x=1472, y=872, c=0x126d96} -- drone选择窗口，确认
    local max_drone_sel = {x=1442, y=534, c=0x535353} -- drone数量选择器中“最多”

    moveZoomOut(660, 340, 1260, 740, 40)
    waitForColor(room_focus_hint, 1000,
        function()
            qcRandTap(production_room, 30)
        end    
    )
    waitForAllColors(room_tags, 300, -- 因为选择点击第一个贸易站，第一个贸易站的tag在总览页面是选中状态
        function () 
            qcRandTap(detail_tag, 50, 700)
        end
    )

    waitForColor(use_drone_confirm, 300, 
        function ()
            qcRandTap(drone_boost, 40, 500)
        end
    )

    waitForAllColors(room_tags, 300,
        function() 
            if qcIsColor(max_drone_sel) then
                qcRandTap(max_drone_sel, 30, 800)
            end
            qcRandTap(use_drone_confirm, 30, 500)
        end
    )

    if _data.infra_mode == "stone" then
        local stone_room = {x=14, y=318, c=0xffd802}
        local manual_fillup = {x=1445, y=300}
        local mf_confirm = {x=1380, y=900, c=0xa5ef}
        waitForColor(stone_room, 100, 
            function ()
                qcRandTap(stone_room, 20, 700)
            end 
        )
        qcRandTap(manual_fillup, 10, 1000)
        waitWhileColor(mf_confirm, 150,
            function ()
                qcRandTap(mf_confirm, 20, 700)
            end
        ) 
    end
    qcKeyBack(700)
    qcKeyBack(1400)
end


local function collect_all()
    local bell_tag = {x=1835, y=135, c=0x2da4db} -- 总览右上角蓝色铃铛标签
    local bell_tag_lower = {x=1835, y=205, c=0x2fa8df} -- 如果有感叹号的话，铃铛就是第二个标签
    local pending_tag_hint = {x=390, y=1012, c=0x27a0d7} -- 可以一键收取的tag，特点是蓝色气泡足够长
    local first_pending_tag = {x=280, y=1030} -- tag中心，以防上述气泡位置太偏点不上

    if qcIsColor(bell_tag) or qcIsColor(bell_tag_lower) then

        if qcIsColor(bell_tag) then
            waitWhileColor(bell_tag, 400, 
                function ()
                    qcRandTap(bell_tag, 10, 1000)
                end
            )
        elseif qcIsColor(bell_tag_lower) then
            waitWhileColor(bell_tag_lower, 400, 
                function ()
                    qcRandTap(bell_tag_lower, 10, 1000)
                end
            )
        end
       
        while qcIsColor(pending_tag_hint) do
            qcRandTap(first_pending_tag, 5, 2000)
        end
        
        qcRandTap(bell_tag, 10, 1200)
    end
end


-- EXPERIMENTS
local function get_character_name_tolerance(i, j)
    local y = card_offset_y + i*card_y_stride
    local x = card_offset_x + j*card_x_stride
    x,_ = fix_coordinate(x,y+card_h, 1, 0, 80, 110)
    local name_x = x + 30
    local name_y = y + 360
    local _x, _y = name_x, name_y
    local tab = {}
    local ret = ""
    local fname = "toler_" .. tostring(i) .."_"..tostring(j)..".png"
    snapshot(fname, _x, _y, _x+155, _y+33)
    -- for _ = 1,20 do
    --     local text = baiduOcr(_x, _y, _x+145, _y+40)
    --     _x = name_x + math.random(6) - 3
    --     _y = name_y + math.random(6) - 3
    --     if has(tab, text) then
    --         table.insert(tab, text)
    --     end
    -- end
    -- local text = tsOcrText(dict, name_x, name_y, name_x + 145, name_y+45, "FFFFFF,5F5F5F", 90)
    -- dialog("target text "..text, 1)
    for _, v in ipairs(tab) do
        ret = ret..v..","
    end
    return ret
end

local function find_tolerance(row)
    ret = ""
    for i = 1,6 do
        slide_cards(1)
        for _ = 1,5 do
            ret = ret .. get_character_name_tolerance(row, 6-i) .."\n"
        end
    end
    return ret
end

local function get_charater_name_tm(i, j)
    if cv == nil then    
        cv = require("libluacv")
    end
    local y = card_offset_y + i*card_y_stride
    local x = card_offset_x + j*card_x_stride
    x,_ = fix_coordinate(x,y+card_h, 1, 0, 80, 110)
    local name_x = x + 45
    local name_y = y + 360
    -- local names = lsdir(userPath() .. "/res/names")
    local src_pic = "tmp.png"
    local sort_tab = {}
    local res_dict = {}
    snapshot(src_pic, _x, _y, _x+140, _y+33)
    for i, name in ipairs(names) do
        local prob = cv.cvMatchDict(tmp_pic, _data.nameBM[name])
        res_tab[i] = prob
        res_dict[prob] = name
    end
    table.sort(res_tab)
    return res_dict[res_tab[-1]]
end


local function find_character_ex(name)
    -- this shit actually works!!
    if cv == nil then
        cv = require("libluacv")
    end
    local snap_file = userPath() .. "/res/tmp.png"
    for n=1,14 do
        snapshot(snap_file, 600,480,1919,950)
        local prob, x, y = cv.cvMatchDict(snap_file, _data.nameBM[name])
        dialog(string.format("best on this page: %f [%d,%d]", prob, x, y),1)
        local is_collide = false
        if prob < 0.2 and _data.collideDict[name] ~= nil then
            for _, collide in ipairs(_data.collideDict[name]) do
                local cp, cx, cy = cv.cvMatchDict(snap_file, _data.nameBM[collide])
                if prob < 0.2 and point_close_enough({x,y}, {cx,cy}) then
                    is_collide = true
                    break
                end
            end 
        end
        if prob >= 0.2 then
            slide_cards(6)
            mSleep(400)
        elseif is_collide then
            for j=0,5 do
                for i=0,1 do
                    if get_character_name(i, j) == name then
                        return i+1, j+1
                    end
                end
            end
            slide_cards(6)
            mSleep(400)
        else
            local idx_x = math.ceil((x+600 - card_offset_x ) / card_x_stride)
            local idx_y = math.ceil((y+480 - card_offset_y ) / card_y_stride)
            dialog(string.format( "do click on [%d,%d]", idx_y, idx_x ),1)
            log(string.format( "find match for %s @ [%d,%d] with prob: %f", name, idx_y, idx_x, prob))
            return idx_y, idx_x
        end
    end
end


local function check_all_characters()
    if cv == nil then    
        cv = require("libluacv")
    end
    local snap_file_tpl = userPath() .. "/res/tmp"
    for i=1,14 do
        local snap_file = snap_file_tpl .. tostring(i) .. ".png"
        local cnt = 0
        local res_tab = {}
        local res_dict = {}
        snapshot(snap_file, 600,480,1919,950)
        for name, bm in pairs(_data.nameBM) do
            local prob = cv.cvMatchDict(snap_file, bm)
            res_tab[cnt] = prob
            res_dict[prob] = name
            cnt = cnt + 1
        end
        table.sort(res_tab)
        log("---- all chara match for page " .. tostring(i) .. " ----")
        local cnt = 0
        for _, p in ipairs(res_tab) do
            local s = ""
            if cnt < 12 then
                s = "* "
            end
            log(string.format( "%s%s : %f ", s, res_dict[p], p ))
            cnt = cnt + 1
        end
        toast("page " .. tostring(i) .. " finished!", 1)
        slide_cards(6)
        mSleep(500)
    end
end

-- local n = "阿消"
-- dialog(string.format("tolerance: %s", find_tolerance(0)))
-- slide_floor(2,true)

-- slide_floor(5, true)

-- slide_floor(4, false)
-- slide_floor(1, true)

-- slide_floor(3, false)
-- click_room(1,3,floor_anchor_offset[5])
-- mSleep(1000)

-- click_card(1,1)
-- for n, bm in pairs(_data.nameBM) do
    -- toast("click " .. n, 1)
    -- local i,j = find_character_ex(n)
    -- while i == nil do
    --     reset_slide_card()
    --     mSleep(400)
    --     i,j = find_character_ex(n)
    -- end
    -- local ai_name = get_character_name(i-1,j-1)
    -- if ai_name == n then
    --     click_card(i,j)
    --     mSleep(800)
    --     click_card(i,j)
    -- else
    --     dialog(string.format("find %s but got %s", n, ai_name))
    --     find_and_click_characters({n})
    -- end
    -- reset_slide_card()
    -- mSleep(500)
-- end

-- mSleep(3000)
-- check_all_characters()

-- local rest = {}
-- local state_tab = {control = {shift_idx=1, on_duty=3},exp = {shift_idx=2, on_duty=2}, gold = {shift_idx=3, on_duty=3}}
-- roomFunction.pipeline(state_tab.exp, "exp", rest)
-- dialog(table.concat( rest, ", "))
-- dialog(string.format( "shift_idx = %d, on_duty = %d", state_tab.exp.shift_idx, state_tab.exp.on_duty))

local auto_infra = {}

auto_infra.run = function()
    collect_all()
    log("[DATE][arknights/auto_infra] 下轮可用干员 : "..table.concat( do_shift(), ", "), "infra")

    if _data.drone_strategy == 'trade' then
        exhaust_drones_trade() -- 243基建需要加速贸易站
    elseif _data.drone_strategy == 'gold' then
        exhaust_drones_product() -- 333基建需要加速摸金
    end 

    collect_all()
end

--doAt({"08:00", "16:00", "00:00"}, auto_infra.run)
if __name__ == 'main' then
    return auto_infra
else
    -- exhaust_drones_trade()
    init(1)
    auto_infra.run()
end
